package com.qiall.oms.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.qiall.common.request.BaseResponse;
import com.qiall.fileutil.FileInfoUtil;
import com.qiall.oms.GateWayReloadService;
import com.qiall.oms.ability.ReportDubboService;
import com.qiall.oms.ability.SetDubboClientClassService;
import com.qiall.oms.ability.request.ReportDubboRequest;
import com.qiall.oms.service.preperties.OmsProperties;
import com.qiall.uriutil.IPUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.common.deploy.DeployState;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.spring.context.event.DubboApplicationStateEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;


@Component
@Slf4j
public class DubboServiceReportListener {

    @DubboReference(check = false, version = "${dubbo.provider.version}")
    private ReportDubboService reportDubboService;

    @Autowired
    private ApplicationContext applicationContext;

    @Value("${dubbo.provider.group}")
    private String providerGroup;

    @Value("${dubbo.application.qos-port}")
    private Integer port;

    @Value("${dbuuo.provider.ip}")
    private String ip;
    @Value("${dubbo.application.name}")
    private String applicationName;

    @Value(("${dubbo.provider.version}"))
    private String version;

    @Autowired
    private OmsProperties omsProperties;

    public static String BASE_PACKAGE = "com.qiall";
    private final String RESOURCE_PATTERN = "/**/*.class";

    @EventListener(classes = {DubboApplicationStateEvent.class})
    public void run(DubboApplicationStateEvent event) {
//        if (omsProperties.getClientUploadClient() == false) {
//            return;
//        }
        if (!event.getState().equals(DeployState.STARTED)) {
            return;
        }
        ReportDubboRequest request = new ReportDubboRequest();
        if (ip != null) {
            request.setIp(ip);
        } else {
            //todo 可能会出现异常
            String ip = IPUtil.getLocalIpAddress();
            request.setIp(ip);
        }
        request.setPort(port);
        if (!providerGroup.equals("$dubbo.provider.group")) {
            request.setDefaultGroup(providerGroup);
        } else {
            request.setDefaultGroup("");
        }
        request.setApplicationName(applicationName);
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

        String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + RESOURCE_PATTERN;
        MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);

        try {
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            List<ReportDubboRequest.ServiceInfo> serviceInfos = Lists.newArrayList();
            for (Resource resource : resources) {
                MetadataReader reader = readerfactory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                //判断是否有指定主解
                DubboService service = clazz.getAnnotation(DubboService.class);
                // 获取所有注解
                if (service == null) {
                    continue;
                }
                String group = service.group() == null ? providerGroup : service.group();
                // 获取对应的接口
                Class<?>[] classes = clazz.getInterfaces();
                ReportDubboRequest.ServiceInfo serviceInfo = new ReportDubboRequest.ServiceInfo();
                for (Class<?> interfaceTmp : classes) {
                    serviceInfo.setService(interfaceTmp.getName());
                    serviceInfo.setGroup(group);
                    serviceInfo.setVersion(version);
                    if (StringUtils.isNotBlank(service.version())) {
                        serviceInfo.setVersion(service.version());
                    }
                    // 获取所有的方法
                    Method[] methods = interfaceTmp.getMethods();
                    List<ReportDubboRequest.MethodInfo> methodInfos = new ArrayList<>();
                    for (Method method : methods) {
                        ReportDubboRequest.MethodInfo methodInfo = new ReportDubboRequest.MethodInfo();
                        methodInfos.add(methodInfo);
                        methodInfo.setMethodName(method.getName());
                        // 设置参数
                        Class<?>[] params = method.getParameterTypes();
                        List<Object> paramsList = new ArrayList<>();
                        List<String> paramTypesList = new ArrayList<>();
                        methodInfo.setParams(paramsList);
                        methodInfo.setParamTypes(paramTypesList);
                        for (Class<?> param : params) {
                            if (Modifier.isAbstract(param.getModifiers())) {
                                continue;
                            }
                            Object object = getObj(param);
                            if (object == null) {
                                Map<String, Object> data = new HashMap<>();
                                Object tmp = null;
                                try {
                                    tmp = param.newInstance();
                                } catch (Exception e) {
                                    continue;
                                }
                                objToJsonMap(tmp, data);
                                object = data;
                            }
                            paramTypesList.add(param.getName());
                            paramsList.add(object);
                        }
                    }
                    serviceInfo.setMethodsJson(JSON.toJSONString(methodInfos, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullNumberAsZero, SerializerFeature.WriteNullBooleanAsFalse));
                }
                log.info("add service info:{} ", JSON.toJSON(serviceInfo));
                serviceInfos.add(serviceInfo);
            }

            request.setServiceList(serviceInfos);
            reportDubboService.reportDubboService(request);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 可能会转换失败
     *
     * @param param
     * @return
     */
    public Object getObj(Class<?> param) {

        if (param.getName().equals(byte.class.getName())) {
            return 0;
        }
        if (param.getName().equals(boolean.class.getName())) {
            return false;
        }
        if (param.getName().equals(int.class.getName())) {
            return 0;
        }
        if (param.getName().equals(float.class.getName())) {
            return 0.0f;
        }
        if (param.getName().equals(long.class.getName())) {
            return 0;
        }
        if (param.getName().equals(double.class.getName())) {
            return 0.0d;
        }
        if (param.getName().equals(short.class.getName())) {
            return 0;
        }
        if (param.getName().equals(char.class.getName())) {
            return 'a';
        }
        // 包装类型
        if (param.getName().equals(Byte.class.getName())) {
            return 0;
        }
        if (param.getName().equals(Boolean.class.getName())) {
            return false;
        }
        if (param.getName().equals(Integer.class.getName())) {
            return 0;
        }
        if (param.getName().equals(Float.class.getName())) {
            return 0.0f;
        }
        if (param.getName().equals(Long.class.getName())) {
            return 0;
        }
        if (param.getName().equals(Double.class.getName())) {
            return 0.0d;
        }
        if (param.getName().equals(Short.class.getName())) {
            return 0;
        }
        if (param.getName().equals(Character.class.getName())) {
            return 'a';
        }
        if (param.getName().equals(String.class.getName())) {
            return "";
        }
        if (param.getName().equals(Date.class.getName())) {
            return System.currentTimeMillis();
        }
        if (param.getName().equals(Map.class.getName())) {
            return "{}";
        }
        if (param.getName().equals(List.class.getName())) {
            return "[]";
        }

        return null;
    }

    public void objToJsonMap(Object obj, Map<String, Object> map) throws InstantiationException, IllegalAccessException {
        Field[] fields = obj.getClass().getDeclaredFields();
        if (fields.length == 0) {
            return;
        }
        for (Field field : fields) {
            if (Modifier.isAbstract(field.getModifiers())) {
                continue;
            }
            Map<String, Object> tmp = new HashMap(5);
            Object objFiled = getObj(field.getType());
            if (objFiled != null) {
                map.put(field.getName(), objFiled);
                continue;
            }
            map.put(field.getName(), tmp);
            objToJsonMap(field.getType().newInstance(), tmp);
        }
    }

}


